#include "StdAfx.h"
#include "StdFile.h"

using namespace Adaptive::Foundation;
using namespace Adaptive::Foundation::IO;

/*--------------------------------------------------------------------------------
 * Constructor / Dispose / Destructor Methods
 *------------------------------------------------------------------------------*/
#pragma region Constructor / Dispose / Destructor Methods
// <summary>
// Initializes a new instance of the class.
// </summary>
// <remarks>
// This is the default constructor.
// </remarks>
StdFile::StdFile()
{
	_fileHandle = NULL;
	_fileSize = 0;
}
// <summary>
// Initializes a new instance of the class.
// </summary>
// <param name="fileHandle">An <see cref="IntPtr"/> value containing the file or device handle being opened.</param>
StdFile::StdFile( IntPtr fileHandle)
{
	_fileHandle = (FILE*)(fileHandle.ToPointer());
	_fileSize = 0;
}
// <summary>
// Initializes a new instance of the class.
// </summary>
// <param name="fileName">The name of the file to be opened.</param>
StdFile::StdFile( String^ fileName )
{
	CString newFileName = fileName;
	_fileHandle = NULL;
	_fileSize = 0;
	UnmanagedOpen( newFileName, CalculateModeString(FileMode::OpenOrCreate, FileAccess::ReadWrite));
}
// <summary>
// Initializes a new instance of the class.
// </summary>
// <param name="fileName">The name of the file to be opened.</param>
// <param name="mode">A <see cref="FileMode"/> enumerated value indicating how the file is to be opened.</param>
// <param name="access">A <see cref="FileAccess"/> enumerated value indicating the access rights. </param>
// <param name="shareMode">A <see cref="FileShare"/> enumerated value indicating how the file is to be shared.</param>
StdFile::StdFile( String^ fileName, FileMode mode, FileAccess access, FileShare shareMode )
{
	CString newFileName = fileName;
	_fileHandle = NULL;
	_fileSize = 0;
	UnmanagedOpen( newFileName, CalculateModeString(mode, access) );
}
// <summary>
// Class destructor.
// </summary>
StdFile::~StdFile()
{
	if (!Disposed)
	{
		Close();
		_fileHandle = NULL;
		_fileSize = 0;
	}
}
#pragma endregion

/*--------------------------------------------------------------------------------
 * Public Properties
 *------------------------------------------------------------------------------*/
#pragma region Public Properties
// <summary>
// Gets a value indicating whether the file is open.
// </summary>
// <value>
// <b>true</b> if the file is open; otherwise, <b>false</b>.
// </value>
bool StdFile::IsOpen::get()
{
	return (_fileHandle != NULL);
}
// <summary>
// Gets a value indicating whether the end of the file has been reached.
// </summary>
// <value>
// <b>true</b> if the current position is at the end of the file; otherwise, <b>false</b>.
// </value>
bool StdFile::Eof::get()
{
	return ((_fileHandle == NULL) || (feof(_fileHandle)));
}
// <summary>
// Gets or sets the current byte position of the file pointer.
// </summary>
// <value>
// The current read/write position within the file, in bytes.
// </value>
long StdFile::Position::get()
{
	long returnValue = 0;
	fpos_t	pos = 0;

	if (_fileHandle == NULL)
		returnValue = 0;
	else
	{
		fgetpos(_fileHandle, &pos);
		returnValue = (long)pos;
	}
	return returnValue;
}
void StdFile::Position::set(long value)
{
	if (_fileHandle != NULL)
	{
		fsetpos(_fileHandle, (fpos_t*)value);
	}
}
// <summary>
// Gets the size of the file, in bytes.
// </summary>
// <value>
// The total size of the file, in bytes.
// </value>
long StdFile::FileSize::get()
{
	return _fileSize;
}
#pragma endregion

/*--------------------------------------------------------------------------------
 * Public Methods / Functions
 *------------------------------------------------------------------------------*/
#pragma region Public Methods / Functions
// <summary>
// Closes the file, if open.
// </summary>
// <remarks>
// This wraps the C <b>fclose</b> function.
// </remarks>
void StdFile::Close()
{
	if (_fileHandle != NULL)
	{
		fclose(_fileHandle);
		_fileHandle = NULL;
		_fileSize = 0;
	}
}
// <summary>
// Flushes the content of the file buffer to disk.
// </summary>
// <remarks>
// This wraps the C <b>fflush</b> function.
// </remarks>
void StdFile::Flush()
{
	if (_fileHandle != NULL)
	{
		fflush(_fileHandle);
	}
}
// <summary>
// Opens the specified file for read and write access.
// </summary>
// <param name="fileName">The name of the file to be opened.</param>
void StdFile::Open(String^ fileName)
{
	if (_fileHandle == NULL)
	{
		CString newFileName = fileName;
		UnmanagedOpen(newFileName, CalculateModeString(FileMode::OpenOrCreate, FileAccess::ReadWrite) );
	}
}
// <summary>
// Opens the specified file for the specified mode and access.
// </summary>
// <param name="fileName">The name of the file to be opened.</param>
// <param name="mode">A <see cref="FileMode"/> enumerated value indicating how the file is to be opened.</param>
// <param name="access">A <see cref="FileAccess"/> enumerated value indicating the access rights. </param>
// <param name="shareMode">A <see cref="FileShare"/> enumerated value indicating how the file is to be shared.</param>
void StdFile::Open(String^ fileName, FileMode mode, FileAccess access, FileShare shareMode)
{
	if (_fileHandle == NULL)
	{
		CString newFileName = fileName;
		UnmanagedOpen(newFileName, CalculateModeString(mode,access) );
	}
}
// <summary>
// Opens access with the specified file handle.
// </summary>
// <param name="fileHandle">An open file or device handle.</param>
void StdFile::Open(IntPtr fileHandle)
{
	if (_fileHandle == NULL)
	{
		UnmanagedOpen((FILE *)fileHandle.ToInt32());
	}
}
// <summary>
// Provides a static mechanusm for opening a file with the specified name and access parameters.
// </summary>
// <param name="fileName">The name of the file to be opened.</param>
// <param name="mode">A <see cref="FileMode"/> enumerated value indicating how the file is to be opened.</param>
// <param name="access">A <see cref="FileAccess"/> enumerated value indicating the access rights. </param>
// <param name="shareMode">A <see cref="FileShare"/> enumerated value indicating how the file is to be shared.</param>
// <returns>
// A new <see cref="StdFile"/> instance if successful, otherwise, <b>null</b>.
// </returns>
StdFile^ StdFile::OpenFile(String^ fileName, FileMode mode, FileAccess access, FileShare shareMode)
{
	StdFile^ returnValue = nullptr;

	returnValue = gcnew StdFile(fileName, mode, access, shareMode);
	if (!returnValue->IsOpen)
	{
		returnValue->Close();
		returnValue = nullptr;
	}
	return returnValue;
}
// <summary>
// Reads the next character from the stream.
// </summary>
// <remarks>
// This wraps the C <b>_fgettc</b> function.
// </remarks>
// <returns>
// The character read from the stream.
// </returns>
Char StdFile::ReadCharacter()
{
	Char returnValue = 0;
	int rawValue = 0;

	if (_fileHandle != NULL)
	{
		rawValue = fgetc(_fileHandle);
		returnValue = (Char)rawValue;
	}
	return returnValue;
}
// <summary>
// Reads the next wide character from the stream.
// </summary>
// <remarks>
// This wraps the C <b>_fgettwc</b> function.
// </remarks>
// <returns>
// The character read from the stream.
// </returns>
Char StdFile::ReadWideCharacter()
{
	Char returnValue = 0;
	wint_t rawValue = 0;

	if (_fileHandle != NULL)
	{
		rawValue = fgetwc(_fileHandle);
		returnValue = (Char)rawValue;
	}
	return returnValue;
}
// <summary>
// Reads the next byte from the stream.
// </summary>
// <remarks>
// This wraps the C <b>_fgettc</b> function.
// </remarks>
// <returns>
// The character read from the stream.
// </returns>
Byte StdFile::ReadByte()
{
	Byte returnValue = 0;
	byte* data = NULL;

	if (_fileHandle != NULL)
	{
		data = new byte[1];
		fread(data, 1, 1,  _fileHandle);
		returnValue = (Byte)data[0];
		delete(data);
	}
	return returnValue;
}
// <summary>
// Reads a string from the stream.
// </summary>
// <param name="stringLength">The size of the string to read.</param>
// <remarks>
// This wraps the C <b>_fgetts</b> function.
// </remarks>
// <returns>
// The string read from the stream, or <b>null</b> if the operation fails.
// </returns>
String^ StdFile::ReadString( int stringLength )
{
	String^ returnValue = nullptr;
	char *rawData = NULL;

	if ((_fileHandle != NULL) && (stringLength > 0))
	{
		rawData = new char[stringLength];
		fgets(rawData, stringLength, _fileHandle);
		returnValue = gcnew String( rawData );
		
		delete(rawData);
	}
	return returnValue;
}
// <summary>
// Reads a string from the stream.
// </summary>
// <param name="stringLength">The size of the string to read.</param>
// <remarks>
// This wraps the C <b>_fgetws</b> function.
// </remarks>
// <returns>
// The string read from the stream, or <b>null</b> if the operation fails.
// </returns>
String^ StdFile::ReadWideString( int stringLength )
{
	String^ returnValue = nullptr;
	wchar_t *rawData = NULL;

	if ((_fileHandle != NULL) && (stringLength > 0))
	{
		rawData = new wchar_t[stringLength];
		fgetws(rawData, stringLength, _fileHandle);
		returnValue = gcnew String( rawData );
		
		delete(rawData);
	}
	return returnValue;
}
// <summary>
// Writes a string to the stream.
// </summary>
// <param name="textToWrite">The text to be written.</param>
// <remarks>
// This wraps the C <b>_fputs</b> function.
// </remarks>
void StdFile::WriteString( String^ textToWrite )
{
	CString buffered = textToWrite;

	if ((textToWrite != nullptr) && (textToWrite->Length > 0) && (_fileHandle != NULL))
	{
		fputs((char *)buffered.GetBuffer(), _fileHandle);
	}
}
// <summary>
// Writes a string to the stream.
// </summary>
// <param name="textToWrite">The text to be written.</param>
// <remarks>
// This wraps the C <b>_fputws</b> function.
// </remarks>
void StdFile::WriteWideString( String^ textToWrite )
{
	wchar_t* buffer = NULL;

	if ((textToWrite != nullptr) && (textToWrite->Length > 0) && (_fileHandle != NULL))
	{
		buffer = new wchar_t[textToWrite->Length];
		for(int count = 0; count < textToWrite->Length; count++)
			buffer[count] = textToWrite[count];

		fputws(buffer, _fileHandle);
		delete(buffer);
	}
}
// <summary>
// Writes a character to the stream.
// </summary>
// <param name="characterToWrite">The character to be written.</param>
// <remarks>
// This wraps the C <b>_fputc</b> function.
// </remarks>
void StdFile::WriteCharacter(Char characterToWrite )
{
	if (_fileHandle != NULL)
		fputc((int)characterToWrite, _fileHandle);
}
// <summary>
// Writes a unicode character to the stream.
// </summary>
// <param name="characterToWrite">The character to be written.</param>
// <remarks>
// This wraps the C <b>_fputwc</b> function.
// </remarks>
void StdFile::WriteWideCharacter(Char characterToWrite )
{
	if (_fileHandle != NULL)
		fputwc((int)characterToWrite, _fileHandle);
}
// <summary>
// Reads the specified number of bytes from the stream into the 
// specified buffer.
// </summary>
// <param name="buffer">A byte array to be filled with data read from the stream.</param>
// <param name="bufferSize">The number of bytes to be read.</param>
// <remarks>
// This wraps the C <b>fread</b> function.
// </remarks>
// <returns>
// The actual number of bytes read from the stream.
// </returns>
int StdFile::ReadBytes(array<Byte>^ buffer, int bufferSize)
{
	BYTE *rawBuffer = NULL;
	int returnValue = 0;
	int error = 0;

	if ((_fileHandle != NULL) && (buffer != nullptr) && (buffer->Length >= bufferSize))
	{
		rawBuffer = new BYTE[bufferSize];
		clearerr(_fileHandle);
		returnValue = (int)fread(rawBuffer, (size_t)1, (size_t)bufferSize, _fileHandle);
		
		if (!feof(_fileHandle))
		{
			error = ferror(_fileHandle);
		}
		
		for(int count = 0; count < returnValue; count++)
			buffer[count] = (Byte)rawBuffer[count];
		delete(rawBuffer);
	}
	return returnValue;
}
// <summary>
// Writes the byte array to the stream.
// </summary>
// <param name="buffer">A byte array to write to the stream.</param>
// <remarks>
// This wraps the C <b>fwrite</b> function.
// </remarks>
void StdFile::WriteBytes(array<Byte>^ buffer)
{
	BYTE* rawBuffer = NULL;

	if ((_fileHandle != NULL) && (buffer != nullptr))
	{
		rawBuffer = new BYTE[buffer->Length];
		for(int count = 0; count < buffer->Length; count++)
			rawBuffer[count] = (BYTE)buffer[count];

		fwrite(rawBuffer, 1, buffer->Length, _fileHandle);
		delete(rawBuffer);
	}
}
// <summary>
// Moves the file pointer to the specified byte position.
// </summary>
// <param name="offset">The byte offset from the specified position.</param>
// <param name="origin">A <see cref="SeekOrigin"/> enumerated value indicating the position from which the offset is calculated.</param>
// <remarks>
// This wraps the C <b>fseek</b> function.
// </remarks>
void StdFile::Seek( long offset, SeekOrigin origin)
{
	int seekValue = 0;

	if (_fileHandle != NULL)
	{
		switch(origin)
		{
		case SeekOrigin::Begin:
			seekValue = SEEK_SET;
			break;

		case SeekOrigin::Current:
			seekValue = SEEK_CUR;
			break;

		case SeekOrigin::End:
			seekValue = SEEK_END;
			break;
		}

		fseek(_fileHandle, offset, seekValue);
	}
}
// <summary>
// Rewinds the stream.
// </summary>
// <remarks>
// This wraps the C <b>rewind</b> function.
// </remarks>
void StdFile::Rewind()
{
	if (_fileHandle != NULL)
		rewind(_fileHandle);
}

#pragma endregion

/*--------------------------------------------------------------------------------
 * Private Methods / Functions
 *------------------------------------------------------------------------------*/
#pragma region Private Methods / Functions
// Opens the specified handle.
void StdFile::UnmanagedOpen( FILE *fileHandle )
{
	fpos_t value = 0;

	_fileHandle = fileHandle;
	fseek(_fileHandle, 0, SEEK_END);
	fgetpos(_fileHandle, &value);
	_fileSize = (long)value;
	fseek(_fileHandle, 0, SEEK_SET);
}
void StdFile::UnmanagedOpen( CString pszFilename, CString pszMode )
{
	if (_fileHandle == NULL)
	{
		_fileHandle = _wfopen(pszFilename, pszMode);
		if (_fileHandle != NULL)
		{
			FileOpened(this, EventArgs::Empty);
			fpos_t value = 0;

			fseek(_fileHandle, 0, SEEK_END);
			fgetpos(_fileHandle, &value);
			_fileSize = (long)value;

			fseek(_fileHandle, 0, SEEK_SET);
			value = 0;
			fsetpos(_fileHandle, &value);
		}
	}
}
CString StdFile::CalculateModeString(FileMode mode, FileAccess access )
{
	CString modeString = "";

	switch(mode)
	{
	case FileMode::Append:
		modeString.Append(L"a");
		break;

	case FileMode::Create:
		modeString.Append(L"w");
		break;

	case FileMode::CreateNew:
		modeString.Append(L"w");
		break;

	case FileMode::Open:
		modeString.Append(L"r+");
		break;

	case FileMode::OpenOrCreate:
		modeString.Append(L"w+");
		break;

	case FileMode::Truncate:
		modeString.Append(L"a");
		break;

	}
	
	return modeString;
}

#pragma endregion

